package main

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"text/template"

	"github.com/pkg/errors"
)

func makeC_CPP(apiFolder string, quote bool) (err error) {
	c := new(apiConfig)
	c.FolderName = apiFolder
	var preLiter string
	if quote {
		preLiter = "q"
		c.ApiName = "CThostFtdcMdApi"
	} else {
		preLiter = "t"
		c.ApiName = "CThostFtdcTraderApi"
	}

	// TODO Req函数
	tepl, err := template.New("func").Funcs(template.FuncMap{
		"join": func(strs ...string) string {
			str := ""
			for _, v := range strs {
				if len(v) > 0 {
					str += v + ","
				}
			}
			str = strings.ReplaceAll(str, "[]", "") // 行情订阅中的char *xxx[] => xxx
			return strings.TrimSuffix(str, ",")
		},
	}).Parse(`DLL_EXPORT_C_DECL void WINAPI ` + preLiter + `{{ .FuncName }}(` + c.ApiName + `*api{{ if gt (len .FuncParams) 0 }}, {{ .FuncParams }}{{ end }}) { api->{{ .FuncName }}({{ join .FuncParamsFirstValue .FuncParamsOtherValues }}); }`)
	if err != nil {
		return errors.Wrap(err, "new template func")
	}
	// 只处理主动函数
	var funcs string
	for _, fd := range GOLOBAL_FunDefs {
		if !strings.HasPrefix(fd.FuncName, "On") {
			buf := bytes.Buffer{}
			tepl.Execute(&buf, *fd)
			funcs += buf.String() + "\n"
			// fmt.Println(buf.String())
		}
	}

	// TODO Reg 注册函数
	var regs string
	for _, fd := range GOLOBAL_FunDefs {
		if strings.HasPrefix(fd.FuncName, "On") { // 只处理 On 函数
			var line string
			if quote {
				line = fmt.Sprintf("DLL_EXPORT_C_DECL void WINAPI qSet%s(Quote *spi, void* func) { spi->_%s = func; }", fd.FuncName, fd.FuncName)
			} else {
				line = fmt.Sprintf("DLL_EXPORT_C_DECL void WINAPI tSet%s(Trade *spi, void* func) { spi->_%s = func; }", fd.FuncName, fd.FuncName)
			}
			regs += line + "\n"
			// fmt.Println(line)
		}
	}
	// TODO 合成
	var content string
	if quote {
		content = `#include "quote.h"
DLL_EXPORT_C_DECL void *WINAPI qCreateApi() { return CThostFtdcMdApi::CreateFtdcMdApi("./log/"); }
DLL_EXPORT_C_DECL void *WINAPI qCreateSpi() { return new Quote(); }
DLL_EXPORT_C_DECL void *WINAPI qGetApiVersion() { return (void*)CThostFtdcMdApi::GetApiVersion(); }
// req
%s
// reg
%s`
	} else {
		content = `#include "trade.h"
DLL_EXPORT_C_DECL void *WINAPI tCreateApi() { return CThostFtdcTraderApi::CreateFtdcTraderApi("./log/"); }
DLL_EXPORT_C_DECL void *WINAPI tCreateSpi() { return new Trade(); }
DLL_EXPORT_C_DECL void *WINAPI tGetApiVersion() { return (void*)CThostFtdcTraderApi::GetApiVersion(); }
// req
%s
// reg
%s`
	}
	content = fmt.Sprintf(content, funcs, regs)
	if quote {
		// 增加 namespaces 前缀
		content = strings.ReplaceAll(content, "CThostFtdcMdApi", "ctp_sopt::CThostFtdcMdApi")
		os.WriteFile("./c/quote.cpp", []byte(content), os.ModePerm)
	} else {
		// 增加 namespaces 前缀
		content = strings.ReplaceAll(content, "CThostFtdcTraderApi", "ctp_sopt::CThostFtdcTraderApi")
		os.WriteFile("./c/trade.cpp", []byte(content), os.ModePerm)
	}
	return nil
}

// makeC_H 生成C的 .h
func makeC_H(apiFolder string, quote bool) (err error) {
	c := new(apiConfig)
	c.FolderName = apiFolder
	if quote {
		c.FileName = "ThostFtdcMdApi"
		c.LibName = "soptthostmduserapi_se"
	} else {
		c.FileName = "ThostFtdcTraderApi"
		c.LibName = "soptthosttraderapi_se"
	}
	head := `#pragma once
#ifdef _WINDOWS  //64位系统没有预定义 WIN32
#ifdef __cplusplus
#define DLL_EXPORT_C_DECL extern "C" __declspec(dllexport)
#else
#define DLL_EXPORT_DECL __declspec(dllexport)
#endif
#else
#ifdef __cplusplus
#define DLL_EXPORT_C_DECL extern "C"
#else
#define DLL_EXPORT_DECL extern
#endif
#endif

#ifdef _WINDOWS
#define WIN32_LEAN_AND_MEAN             //  从 Windows 头文件中排除极少使用的信息
#include "stddef.h"
#ifdef WIN32
#define WINAPI      __cdecl
#include "../../{{.FolderName}}/{{.FileName}}.h"
#pragma comment(lib, "../{{.FolderName}}/{{.LibName}}.lib")
#else
#define WINAPI      __stdcall
#include "../../{{.FolderName}}/{{.FileName}}.h"
#pragma comment(lib, "../{{.FolderName}}/{{.LibName}}.lib")
#endif
#else
#define WINAPI
#include "../../{{.FolderName}}/{{.FileName}}.h"
#endif`
	tepl, _ := template.New("sopt").Parse(head)
	var buf = bytes.Buffer{}
	tepl.Execute(&buf, c)
	head = buf.String()

	// fmt.Println(head)
	// 只处理 On 函数
	funcOns := make([]*funcDefine, 0)
	for _, fd := range GOLOBAL_FunDefs {
		if strings.HasPrefix(fd.FuncName, "On") {
			funcOns = append(funcOns, fd)
		}
	}

	// TODO 1. 类型 typedef int (WINAPI *FrontConnected)();
	var typedef string
	for _, fd := range funcOns {
		line := fmt.Sprintf("typedef int (WINAPI *%sType)(%s);\n", fd.FuncName, fd.FuncParams)
		typedef += line + "\n"
		// fmt.Print(line)
	}

	// TODO 2. 变量 void *_OnFrontConnected;
	var onVars string
	for _, fd := range funcOns {
		line := fmt.Sprintf("void *_%s;\n", fd.FuncName)
		onVars += line + "\n"
		// fmt.Print(line)
	}

	// TODO 3. 函数virtual void OnFrontConnected(){if (_FrontConnected) ((FrontConnected)_FrontConnected)();}
	var onFuncs string
	teplFunc, err := template.New("cb").Funcs(template.FuncMap{
		"thost": func(str string) bool { // 参数是否 struct
			return strings.HasPrefix(str, "CThost")
		},
	}).Parse(`virtual void {{.FuncName}}({{.FuncParams}})
	{
		if (_{{.FuncName}})
		{
		{{if gt (len .FuncParams) 0}}
			{{if thost .FuncParamsFirstValue}}
			if (_{{.FuncParamsFirstValue}})
				(({{.FuncName}}Type)_{{.FuncName}})({{.FuncParamsFirstValue}}{{if gt (len .FuncParamsOtherValues) 0}},{{.FuncParamsOtherValues}}{{end}});
			else
			{
				{{.FuncParamsFirstType}} f = {};
				(({{.FuncName}}Type)_{{.FuncName}})(&f{{if gt (len .FuncParamsOtherValues) 0}},{{.FuncParamsOtherValues}}{{end}});
			}
			{{else}}
			(({{.FuncName}}Type)_{{.FuncName}})({{.FuncParamsFirstValue}}{{if gt (len .FuncParamsOtherValues) 0}},{{.FuncParamsOtherValues}}{{end}});
			{{end}}
		{{else}}
				(({{.FuncName}}Type)_{{.FuncName}})();
		{{end}}
		}
	}`)
	if err != nil {
		return errors.Wrap(err, "生成 template")
	}
	for _, fd := range funcOns {
		buf := bytes.Buffer{}
		teplFunc.Execute(&buf, *fd)
		onFuncs += buf.String() + "\n"
		// fmt.Println(buf.String())
	}
	// 合成
	var hFileContent string
	if quote {
		hFileContent = `%s
class Quote: ctp_sopt::CThostFtdcMdSpi
{
public:
	// typedef
	%s
	// vars
	%s
	// funcs
	%s
};
`
	} else {
		hFileContent = `%s
class Trade: ctp_sopt::CThostFtdcTraderSpi
{
public:
	// typedef
	%s
	// vars
	%s
	// funcs
	%s
};
`
	}
	lines := fmt.Sprintf(hFileContent, head, typedef, onVars, onFuncs)
	if quote {
		os.WriteFile("./c/quote.h", []byte(lines), os.ModePerm)
	} else {
		os.WriteFile("./c/trade.h", []byte(lines), os.ModePerm)
	}
	return nil
}
